package com.chenwc.minio;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.JSONWriter;
import io.minio.Result;
import io.minio.messages.*;
import okhttp3.*;
import org.apache.commons.lang3.StringUtils;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.activation.MimetypesFileTypeMap;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.ZonedDateTime;
import java.util.*;

/**
 * @author 陈为成
 */
public class MinioTest {

    private static final Logger logger = LoggerFactory.getLogger(MinioTest.class);

    private static final String BUCKET_NAME = "test";

    @BeforeAll
    public static void getMinioClient() {
        MinioUtil.getMinioClient("http://192.168.100.129:9000",
                "6ABaREo6aw7dLp9jXfpc",
                "ASqAwPLxWs2rHC9fWH9jUQBtoIVf9NH3sVRM2MxR", "cn-north-1");
    }

    @AfterAll
    public static void disconnect() {
        MinioUtil.close();
    }

    @Test
    @Tag("bucket")
    @DisplayName("判断bucket是否存在")
    public void testBucketExists() {
        logger.info("bucket: {} Exists: {}", BUCKET_NAME, MinioUtil.bucketExists(BUCKET_NAME));
    }

    @Test
    @Tag("bucket")
    @DisplayName("创建bucket")
    public void testCreateBucket() {
        String bucketName = "test";
        MinioUtil.createBucket(bucketName, "cn-north-1", true);
        logger.info("bucket: {} Exists: {}", bucketName, MinioUtil.bucketExists(bucketName));
    }

    @Test
    @Tag("bucket")
    @DisplayName("获取所有bucket")
    public void testListBucket() {
        List<Bucket> bucketList = MinioUtil.listBuckets();
        if (bucketList != null) {
            for (Bucket bucket : bucketList) {
                logger.info("bucket: {}", bucket.name());
            }
        }
    }

    @Test
    @Tag("bucket")
    @DisplayName("获取bucket所有对象")
    public void testListObjects() {
        JSONArray results = MinioUtil.listObjects(BUCKET_NAME, "", false, true);
        logger.info("results: {}", results);
    }

    @Test
    @Tag("bucket")
    @DisplayName("isExistObject")
    public void isExistObject() {
        boolean isExist = MinioUtil.isExistObject(BUCKET_NAME, "test/1712762116966.png");
        logger.info("isExist: {}", isExist);
    }

    @Test
    @Tag("bucket")
    @DisplayName("监听bucket的事件通知")
    public void testListenBucketNotification() {
        MinioUtil.listenBucketNotification(BUCKET_NAME, "", "",
                new String[]{"s3:ObjectCreated:*", "s3:ObjectAccessed:*"});
    }

    @Test
    @Tag("bucket")
    @DisplayName("测试添加bucket生命周期配置")
    public void testSetBucketLifecycle() {
        List<LifecycleRule> rules = new LinkedList<>();
        rules.add(
                new LifecycleRule(
                        Status.ENABLED,
                        null,
                        new Expiration((ZonedDateTime) null, 365, null),
                        new RuleFilter("logs/"),
                        "rule2",
                        null,
                        null,
                        null));
        LifecycleConfiguration config = new LifecycleConfiguration(rules);
        MinioUtil.setBucketLifecycle(BUCKET_NAME, config);
    }

    @Test
    @Tag("bucket")
    @DisplayName("测试获取bucket生命周期配置")
    public void testGetBucketLifecycle() {
        LifecycleConfiguration config = MinioUtil.getBucketLifecycle(BUCKET_NAME);
        if (config != null) {
            for (LifecycleRule rule : config.rules()) {
                logger.info("ruleId: {}", rule.id());
            }
        }
    }

    @Test
    @Tag("bucket")
    @DisplayName("测试添加bucket的通知配置")
    public void testSetBucketNotification() {
        List<EventType> eventList = new LinkedList<>();
        //需要发送通知的事件类型
        eventList.add(EventType.OBJECT_CREATED_PUT);
        eventList.add(EventType.OBJECT_CREATED_COPY);

        QueueConfiguration queueConfiguration = new QueueConfiguration();
        //队列名称
        queueConfiguration.setQueue("arn:minio:sqs::1:webhook");
        //事件类型
        queueConfiguration.setEvents(eventList);
        //前缀
        queueConfiguration.setPrefixRule("images");
        //后缀
        queueConfiguration.setSuffixRule("png");

        List<QueueConfiguration> queueConfigurationList = new LinkedList<>();
        queueConfigurationList.add(queueConfiguration);

        NotificationConfiguration config2 = new NotificationConfiguration();
        config2.setQueueConfigurationList(queueConfigurationList);
        MinioUtil.setBucketNotification(BUCKET_NAME, config2);
    }

    @Test
    @Tag("bucket")
    @DisplayName("测试设置bucket的策略配置")
    public void testSetBucketPolicy() {
        String policy = "{\n" +
                "  \"Statement\": [\n" +
                "    {\n" +
                "      \"Action\": [\n" +
                "        \"s3:GetBucketLocation\",\n" +
                "        \"s3:ListBucket\"\n" +
                "      ],\n" +
                "      \"Effect\": \"Allow\",\n" +
                "      \"Principal\": \"*\",\n" +
                "      \"Resource\": \"arn:aws:s3:::" + BUCKET_NAME + "\"\n" +
                "    },\n" +
                "    {\n" +
                "      \"Action\": \"s3:GetObject\",\n" +
                "      \"Effect\": \"Allow\",\n" +
                "      \"Principal\": \"*\",\n" +
                "      \"Resource\": \"arn:aws:s3:::" + BUCKET_NAME + "/*\"\n" +
                "    }\n" +
                "  ],\n" +
                "  \"Version\": \"2012-10-17\"\n" +
                "}";
        MinioUtil.setBucketPolicy(BUCKET_NAME, policy);
    }

    @Test
    @Tag("bucket")
    @DisplayName("测试设置bucket的版本配置")
    public void testSetBucketVersioning() {
        VersioningConfiguration config = new VersioningConfiguration(
                VersioningConfiguration.Status.ENABLED, true);
        MinioUtil.setBucketVersioning(BUCKET_NAME, config);
    }
    @Test
    @Tag("bucket")
    @DisplayName("测试获取bucket的版本配置")
    public void getBucketVersioning() {
        VersioningConfiguration versioningConfiguration = MinioUtil.getBucketVersioning(BUCKET_NAME);
        if (versioningConfiguration != null){
            if (StringUtils.isEmpty(versioningConfiguration.status().toString())){
                logger.info("Versioning status is OFF!");
            }
            else {
                logger.info("status: {}", versioningConfiguration.status().toString());
            }
            if (versioningConfiguration.isMfaDeleteEnabled() != null){
                logger.info("mfaDelete: {}", versioningConfiguration.isMfaDeleteEnabled());
            }
        }
        else {
            logger.info("versioningConfiguration is null");
        }
    }



    @Test
    @Tag("bucket")
    @DisplayName("测试设置bucket的复制配置")
    public void testSetBucketReplication() {
        Map<String, String> tags = new HashMap<>();
        tags.put("key1", "value1");
        tags.put("key2", "value2");

        ReplicationRule rule =
                new ReplicationRule(
                        new DeleteMarkerReplication(Status.DISABLED),
                        new ReplicationDestination(
                                null,
                                null,
                                "http://localhost:9000/test",
                                null,
                                null,
                                null,
                                StorageClassEnum.STANDARD.getValue()),
                        null,
                        new RuleFilter(new AndOperator("TaxDocs", tags)),
                        "rule1",
                        null,
                        1,
                        null,
                        Status.ENABLED);

        List<ReplicationRule> rules = new LinkedList<>();
        rules.add(rule);

        ReplicationConfiguration config =
                new ReplicationConfiguration("REPLACE-WITH-ACTUAL-ROLE", rules);
        MinioUtil.setBucketReplication(BUCKET_NAME, config);
    }

    @Test
    @Tag("bucket")
    @DisplayName("测试设置bucket的标签配置")
    public void testSetBucketTags() {
        Map<String, String> tags = new HashMap<>();
        tags.put("key1", "value1");
        tags.put("key2", "value2");
        MinioUtil.setBucketTags(BUCKET_NAME, tags);
    }

    @Test
    @Tag("bucket")
    @DisplayName("测试getObject")
    public void getObject() {
        JSONObject resp = MinioUtil.getObject(BUCKET_NAME, "1711893101642.jpg");
        logger.info("resp: {}", JSON.toJSONString(resp, JSONWriter.Feature.WriteMapNullValue));
    }

    @Test
    @Tag("bucket")
    @DisplayName("测试downloadObject")
    public void downloadObject() {
        MinioUtil.downloadObject(BUCKET_NAME, "1711893101642.jpg", "D:\\Downloads\\" + System.currentTimeMillis() + ".png");
    }

    @Test
    @Tag("bucket")
    @DisplayName("测试getPresignedObjectUrl")
    public void getPresignedObjectUrl() {
        String url = MinioUtil.getPresignedObjectUrl(BUCKET_NAME, "1711893101642.jpg");
        logger.info("resp: {}", url);
    }

    @Test
    @Tag("bucket")
    @DisplayName("getPresignedPostFormData")
    public void getPresignedPostFormData() throws IOException {

        File file = new File("D:\\Downloads\\highlight.js-11.9.0.tar.gz");
        String key = file.getName();

        //生成使用post方法上传对象需要用到的表单数据
        //文件默认大小范围在64kiB to 10MiB
        //startsWithCondition是请求头Content-Type的起始条件，这里设置了image/，下面的Content-Type必须是image/开头
        Map<String, String> formData = MinioUtil.getPresignedPostFormData(BUCKET_NAME, key, "");
        logger.info("resp: {}", JSON.toJSONString(formData, JSONWriter.Feature.WriteMapNullValue));

        // Upload an image using POST object with form-data.
        MultipartBody.Builder multipartBuilder = new MultipartBody.Builder();
        multipartBuilder.setType(MultipartBody.FORM);
        if (formData != null) {
            for (Map.Entry<String, String> entry : formData.entrySet()) {
                multipartBuilder.addFormDataPart(entry.getKey(), entry.getValue());
            }
        }

        multipartBuilder.addFormDataPart("key", key);
        multipartBuilder.addFormDataPart("Content-Type", "multipart/form-data");

        String contentType = new MimetypesFileTypeMap().getContentType(file);
        if (contentType == null || contentType.isEmpty()) {
            contentType = "application/octet-stream";
        }
        // "file" 必须最后添加
        multipartBuilder.addFormDataPart(
                "file", file.getName(), RequestBody.create(file, MediaType.parse(contentType)));

        Request request =
                new Request.Builder()
                        .url("http://192.168.100.129:9000/" + BUCKET_NAME)
                        .post(multipartBuilder.build())
                        .build();
        OkHttpClient httpClient = new OkHttpClient().newBuilder().build();
        try (Response response = httpClient.newCall(request).execute()) {
            if (response.body() != null) {
                logger.info("response: {}", new String(Objects.requireNonNull(response.body()).bytes(), StandardCharsets.UTF_8));
            }
            if (response.isSuccessful()) {
                logger.info(file.getName() + " 使用 POST 成功上传！");
            } else {
                logger.info("上传失败 " + file.getName());
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

    @Test
    @Tag("bucket")
    @DisplayName("putObject")
    public void putObject() {
        File file = new File("D:\\Downloads\\1711893101642.jpg");
        // Upload input stream with headers and user metadata.
        Map<String, String> headers = new HashMap<>();
        headers.put("X-Amz-Storage-Class", StorageClassEnum.REDUCED_REDUNDANCY.getValue());
        Map<String, String> userMetadata = new HashMap<>();
        userMetadata.put("My-Project", "Project One");
        MinioUtil.putObject(BUCKET_NAME, file.getName(), file,
                headers, userMetadata, MediaType.parse("image/jpg"));
    }

    @Test
    @Tag("bucket")
    @DisplayName("uploadObject")
    public void uploadObject() {
        File file = new File("D:\\Downloads\\1711893101642.jpg");
        MinioUtil.uploadObject(BUCKET_NAME, file.getName(), file.getAbsolutePath());
    }

    @Test
    @Tag("bucket")
    @DisplayName("uploadSnowballObjects")
    public void uploadSnowballObjects() {
        File file = new File("D:\\Downloads\\ideaIU-2023.3.6.exe");
        //分块上传对象
        MinioUtil.uploadSnowballObjects(BUCKET_NAME, file.getName(), file);
    }

    @Test
    @Tag("bucket")
    @DisplayName("selectCsvObjectContent")
    public void selectCsvObjectContent() {
        String resp = MinioUtil.selectCsvObjectContent(BUCKET_NAME, "chenweic0930.csv.gz",
                CompressionType.GZIP, "select * from S3Object limit 5");
        logger.info("resp: {}", resp);
    }

    @Test
    @Tag("bucket")
    @DisplayName("selectJsonObjectContent")
    public void selectJsonObjectContent() {
        String resp = MinioUtil.selectJsonObjectContent(BUCKET_NAME, "W4000.json.gz",
                CompressionType.GZIP, JsonType.DOCUMENT,"select * from S3Object");
        logger.info("resp: {}", resp);
    }

    @Test
    @Tag("bucket")
    @DisplayName("setObjectRetention")
    public void setObjectRetention() {
        //要先设置ObjectLockConfiguration，否则会报错
        Retention retention = new Retention(RetentionMode.COMPLIANCE, ZonedDateTime.now().plusYears(1));
        MinioUtil.setObjectRetention("test3", "1712762116966.png", retention);
    }

    @Test
    @Tag("bucket")
    @DisplayName("statObject")
    public void statObject() {
        JSONObject resp = MinioUtil.statObject("test3", "1712762116966.png", null);
        logger.info("resp: {}", resp.toJSONString(JSONWriter.Feature.WriteMapNullValue));
    }

    @Test
    @Tag("bucket")
    @DisplayName("getObjectVersion")
    public void getObjectVersion() {
        JSONArray resp = MinioUtil.getObjectVersion("test", "1711893101642.jpg");
        logger.info("resp: {}", resp.toJSONString(JSONWriter.Feature.WriteMapNullValue));
    }

    @Test
    @Tag("bucket")
    @DisplayName("removeObjects")
    public void removeObjects() {
        List<String> keys = new ArrayList<>();
        keys.add("1711893101642.jpg");
        keys.add("1712762116966.png");
        MinioUtil.removeObjects(BUCKET_NAME, keys);
    }

    @Test
    @Tag("bucket")
    @DisplayName("removeObjectsByVersionId")
    public void removeObjectsByVersionId() {
        List<Map<String, String>> keys = new ArrayList<>();
        Map<String, String> key1 = new HashMap<>();
        key1.put("key", "1711893101642.jpg");
        key1.put("versionId", "");
        keys.add(key1);
        Map<String, String> key2 = new HashMap<>();
        key2.put("key", "1712762116966.png");
        key2.put("versionId", "");
        keys.add(key2);
        MinioUtil.removeObjectsByVersionId(BUCKET_NAME, keys);
    }
}
